﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace TrafficApp
{
    public class App
    {
        #region Vars

        enum phase
        {
            EditMode,
            Simulation,
            PausedSim
        }
        public static int carIndex = 0;
        public static App app;
        /// <summary>
        /// Main timer, will send ticks to anything that needs it
        /// </summary>
        public static Timer MainTimer;

        /// <summary>
        /// shows how much time has passed
        /// </summary>
        public static int timePassed;

        /// <summary>
        /// Random generator accessible through
        /// </summary>
        public static Random r;

        /// <summary>
        /// list of traffic
        /// </summary>
        private List<Car> cars = new List<Car>();

        /// <summary>
        /// list of crossings
        /// </summary>
        private List<Crossing> crossings;// = new List<Crossing>();

        /// <summary>
        /// list of roads
        /// </summary>
        private List<Road> Roads = new List<Road>();

        /// <summary>
        /// Worksheet Grid with cells for the app
        /// </summary>
        private Grid mainGrid;

        private PictureBox pb;
        /// <summary>
        /// Holding the cell loc to show of mouse focus 
        /// </summary>
        protected Point hoverCellLoc = new Point (-1,-1);
        protected Point selectCellLoc = new Point(-1, -1);

        #endregion
        #region Methods

        /// <summary>
        /// Constructor with grid's specified nr of cells(180x180)
        /// </summary>
        /// <param name="rows">Number of rows</param>
        /// <param name="cols">Number of columns</param>
        public App(int nrOfComponents, PictureBox pictureBox)
        {
            app = this;
            MainTimer = new Timer();
            MainTimer.Interval = 1;
            MainTimer.Tick += new EventHandler(Tmr_Tick);
            pb = pictureBox;
            r = new Random();
            // Validate nr of components
            if (nrOfComponents > 18)
                nrOfComponents = 18;

            mainGrid = new Grid(3, 6); // max
            crossings = new List<Crossing>(nrOfComponents);
        }

        /// <summary>
        /// this method draws the situation
        /// </summary>
        public void Draw(object sender, PaintEventArgs e)
        {
            Image image;
            foreach (Crossing c in crossings)
            {
                image = Image.FromFile(@"..\..\Resources\" + c.Type+ ".png");
                e.Graphics.DrawImage(image, c.loc.Xint, c.loc.Yint, Grid.CELL_SIZE, Grid.CELL_SIZE);

                foreach (CarTL ctl in c.Carlights)
                {
                    SolidBrush brush = new SolidBrush(ctl.Clr);
                    e.Graphics.FillEllipse(brush, ctl.light);
                }
                foreach (PedesTL ptl in c.PedesLights)
                {
                    SolidBrush brush = new SolidBrush(ptl.Clr);
                    e.Graphics.FillEllipse(brush, ptl.light);
                }
                if (c.Type == "Cross2")
                {
                    //draw pedestrians
                    foreach (Pedestrian p in c.Pedestrians)
                    {
                        SolidBrush brush = new SolidBrush(Color.Magenta);
                        e.Graphics.FillRectangle(brush, new Rectangle((int)p.loc.X, (int)p.loc.Y, 3, 3));   
                    }
                }
            }

            //draw cars
            foreach (Car c in cars)
            {
                SolidBrush brush = new SolidBrush(c.Clr);
                e.Graphics.FillRectangle(brush, new Rectangle((int)c.loc.X, (int)c.loc.Y, 10, 10));   
            }


            // Hover and Select cells in the main panel (pbxMain)
            #region hover and select 
            Rectangle cellRect = new Rectangle(hoverCellLoc.X, hoverCellLoc.Y, Grid.CELL_SIZE, Grid.CELL_SIZE);
            Pen myPen = new Pen(Color.LimeGreen, 1);
            SolidBrush myBrush = new SolidBrush(Color.FromArgb(10, Color.SkyBlue));
            if (hoverCellLoc.X != -1)
            {
                e.Graphics.DrawRectangle(myPen, cellRect);
                e.Graphics.FillRectangle(myBrush, cellRect); 
            }

            //cellRect = new Rectangle(selectCellLoc.X, selectCellLoc.Y, Grid.CELL_SIZE, Grid.CELL_SIZE);
            myPen = new Pen(Color.Red, 1);
            myBrush = new SolidBrush(Color.FromArgb(10, Color.Red));
            if (selectCellLoc.X != -1)
            {
                e.Graphics.DrawRectangle(myPen, cellRect);
                e.Graphics.FillRectangle(myBrush, cellRect);
            }

            myPen.Dispose();
            myBrush.Dispose();
            #endregion
        }

        public void Draw(PictureBox pb) // For smooth drawing
        {
            pb.Invalidate();
        }


        /// <summary>
        /// updates the Traffic
        /// </summary>
        public void UpdateTraffic()
        {
        }

        #region Simulation

        /// <summary>
        /// Starts simulation
        /// </summary>
        public void StartSim()
        {
            MainTimer.Enabled = true;

        }

        /// <summary>
        /// Pauses simulation
        /// </summary>
        public void PauseSim()
        {
            MainTimer.Enabled = false;
        }

        /// <summary>
        /// stops simulation
        /// </summary>
        public void StopSim()
        {
            MainTimer.Enabled = false;
        }

        #endregion
        #region Editing

        /// <summary>
        /// Adds crossing
        /// </summary>
        public bool AddCrossing(Vector Loc, string type)
        {
            if (MainTimer.Enabled)
                return false;

            // TODO validation if x and y are out of window, although it's done in MainForm
            if (!isComponent(Loc) && type != "")
            {
                Loc = mainGrid.relocate(Loc);
                Crossing currCross = new Crossing(Loc, type);
                if (crossings.Count == crossings.Capacity)
                    return false;
                    crossings.Add(currCross);
                return true;
            }
            return false;
        }
        /// <summary>
        /// Add crossing in the next available loc (helper uses AddCrosing(Vector, type))
        /// </summary>
        public bool AddCrossing(string type)
        {
            if (MainTimer.Enabled)
                return false;

            // If there is no elements just add in the first place
            if (crossings.Count == 0)
            {
                return AddCrossing(new Vector(), type);
            }

            if (crossings.Count == crossings.Capacity)
                return false;

            for (int y = 0; y < 3 * Grid.CELL_SIZE; y += Grid.CELL_SIZE)
            {
                for (int x = 0; x < 6 * Grid.CELL_SIZE; x += Grid.CELL_SIZE)
                {
                    foreach (Crossing c in crossings)
                    {
                        Vector tempLoc = new Vector(x, y);
                        if (!isComponent(tempLoc))
                        {
                            return AddCrossing(tempLoc, type);
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Adds a road
        /// </summary>
        public void AddRoad(Vector Loc)
        {
        }

        /// <summary>
        /// Deletes selected crossing 
        /// </summary>
        public bool DelCrossing()
        {
            if (MainTimer.Enabled)
                return false;

            Vector loc = new Vector(selectCellLoc);

            // check if selected location has component
            if (!isComponent(loc)) 
                return false;

                foreach (Crossing c in crossings)
                {
                    if (c.loc == loc)
                    {
                        crossings.Remove(c);
                        return true;
                    }
                }
                return false;
        }

        /// <summary>
        /// Deletes road
        /// </summary>
        public void DelRoad(Vector Loc)
        {
        }

        /// <summary>
        /// Adds a corner to the road
        /// </summary>
        public void AddTurn(Vector Loc)
        {
        }

        /// <summary>
        /// finishes the road
        /// </summary>
        public void FinishRoad(Vector Loc)
        {
        }

        #endregion
        #region Menu

        /// <summary>
        /// saves worksheet
        /// </summary>
        public void SaveGrid()
        {
        }

        /// <summary>
        /// loads worksheet
        /// </summary>
        public void LoadGrid()
        {
        }

        /// <summary>
        /// new worksheet
        /// </summary>
        public void NewGrid()
        {
        }

        /// <summary>
        /// move method
        /// </summary>
        public void MoveCars()
        {
            for (int i = 0; i < cars.Count; i++)
            {
                crossChckr(cars[i]);
                cars = cars[i].Move(cars);
            }
        }

        #endregion
        #endregion
        private void Tmr_Tick(object sender, EventArgs e)
        {
            timePassed++;
            //Regulate TL          
            foreach (Crossing c in crossings)
            {
                c.TLRegulation(timePassed);
                foreach (Garage g in c.CarGenerators)
                {

                    if (timePassed % 500 == 1)
                        cars.Add(g.carGen());

                }

                if (c.Type == "Cross2")
                {
                    //move pedestrians
                    for (int i = 0; i < c.Pedestrians.Count; i++)
                    {
                        c.Pedestrians[i].Move(c, c.Pedestrians);
                    }

                    if (timePassed % 50 == 1)
                    {
                        c.Pedestrians.Add(new Pedestrian((new Vector(c.loc.X + 64, c.loc.Y + 50)), (new Vector(1, 0))));
                        c.Pedestrians.Add(new Pedestrian((new Vector(c.loc.X + 63, c.loc.Y + 53)), (new Vector(1, 0))));
                        c.Pedestrians.Add(new Pedestrian((new Vector(c.loc.X + 62, c.loc.Y + 47)), (new Vector(1, 0))));
                    }
                }
            }
            MoveCars();
            //Draw
            this.Draw(this.pb);
        }

        /// <summary>
        /// Checks if given coords contain component (Cross, Road, ...)
        /// </summary>
        public bool isComponent(Vector loc)
        {
            loc = mainGrid.relocate(loc); // valid cell Point

            foreach (Crossing c in crossings)
            {
                if (loc == c.loc)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Returns Type (Cross, Road, ...) of Component in given coords 
        /// </summary>
        /// <returns></returns>
        public string getType(Vector loc)
        {
            loc = mainGrid.relocate(loc); // valid cell Point

            foreach (Crossing c in crossings)
            {
                if (loc == c.loc)
                    return c.Type;
            }
            return "";
        }

        /// <summary>
        /// Returns Type (Cross, Road, ...) of Component in given coords 
        /// </summary>
        /// <returns></returns>
        public bool moveComponent(Vector currCoords, Vector newCoords)
        {
            if (MainTimer.Enabled)
                return false;
            //TODO works only when simulation is not running
            currCoords = mainGrid.relocate(currCoords);
            newCoords = mainGrid.relocate(newCoords);

            // If no new Location or if there is component in the new location don't move
            if ((currCoords == newCoords) || isComponent(newCoords))
                return false;

            foreach (Crossing c in crossings)
            {
                if (currCoords == c.loc)
                {
                    string tempCrossType = c.Type;
                    crossings.Remove(c);
                    crossings.Add(new Crossing(newCoords, tempCrossType));
                    return true;
                }
            }
            return false;
        }

        public void hoverCell(Point loc)
        {
            if (loc.X == 0)
                hoverCellLoc.X = -1;
            else
            {
                loc = mainGrid.relocate(loc);
                hoverCellLoc = loc;
            }
        }
        public void selectCell(Point loc)
        {
            if (loc.X == 0)
                selectCellLoc.X = -1;
            else
            {
                loc = mainGrid.relocate(loc);
                selectCellLoc = loc;
            }
        }
        public Crossing getCrossing(Vector Loc)
        {
            foreach (Crossing c in crossings)
            {
                if (c.loc.X <= Loc.X && c.loc.X + 180 > Loc.X
                    && c.loc.Y <= Loc.Y && c.loc.Y + 180 > Loc.Y)
                {
                    return c;
                }
            }
            return null;
        }
        private void crossChckr(Car c)
        {
            // Checking on which crossing each car is
            foreach (Crossing cr in crossings)
            {
                if (cr.loc.Xint / 180 == c.loc.Xint / 180 & cr.loc.Yint / 180 == c.loc.Yint / 180)
                {
                    c.Cross = cr;
                    return;
                }
            }
            c.Cross = null;
        }
    }
}
